package gorpc

import (
	"fmt"
	"net/http"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gorilla/websocket"
)

// RPCServer represents a WebSocket RPC server
type RPCServer struct {
	mu sync.RWMutex

	// Server configuration
	addr     string
	upgrader websocket.Upgrader

	// Session management
	sessions    sync.Map
	sessionID   atomic.Int64
	maxSessions int32

	// Callback handler
	callback RPCCallback

	// HTTP server
	server *http.Server
}

// NewRPCServer creates a new RPC server instance
func NewRPCServer(addr string, callback RPCCallback) *RPCServer {
	if callback == nil {
		callback = &BaseCallback{}
	}

	return &RPCServer{
		addr: addr,
		upgrader: websocket.Upgrader{
			CheckOrigin: func(r *http.Request) bool {
				return true // Allow all origins
			},
		},
		maxSessions: 1000, // Default max sessions
		callback:    callback,
	}
}

// SetMaxSessions sets the maximum number of concurrent sessions
func (s *RPCServer) SetMaxSessions(max int32) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.maxSessions = max
}

// Start starts the RPC server
func (s *RPCServer) Start() error {
	s.mu.Lock()
	if s.server != nil {
		s.mu.Unlock()
		return fmt.Errorf("server already started")
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/", s.handleWebSocket)

	s.server = &http.Server{
		Addr:    s.addr,
		Handler: mux,
	}
	s.mu.Unlock()

	return s.server.ListenAndServe()
}

// Stop stops the server and closes all sessions
func (s *RPCServer) Stop() error {
	// Close all sessions
	s.sessions.Range(func(key, value interface{}) bool {
		if session, ok := value.(*RPCSession); ok {
			session.Close()
		}
		s.sessions.Delete(key)
		return true
	})

	// Close HTTP server
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.server != nil {
		err := s.server.Close()
		s.server = nil
		return err
	}
	return nil
}

// handleWebSocket handles incoming WebSocket connections
func (s *RPCServer) handleWebSocket(w http.ResponseWriter, r *http.Request) {
	s.mu.RLock()
	maxSessions := s.maxSessions
	s.mu.RUnlock()

	// Check session limit
	if s.sessionID.Load() >= int64(maxSessions) {
		http.Error(w, "server is busy", http.StatusServiceUnavailable)
		return
	}

	// Upgrade connection
	conn, err := s.upgrader.Upgrade(w, r, nil)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Create session
	session := NewRPCSession(conn)
	sessionID := s.sessionID.Add(1)
	s.sessions.Store(sessionID, session)

	// Set callbacks
	session.SetConnectedCallback(func() {
		s.callback.OnConnect(session)
	})

	session.SetDisconnectedCallback(func(err error) {
		errCode := 0
		errMsg := ""
		if err != nil {
			errCode = -1 // Default error code
			errMsg = err.Error()
		}
		s.callback.OnClose(session, errCode, errMsg)
		s.sessions.Delete(sessionID)
		s.sessionID.Add(-1) // Decrease session count
	})

	session.SetRequestCallback(func(inMsg *RPCMessage, outMsg *RPCMessage) error {
		return s.callback.OnRequest(session, inMsg, outMsg, 30*time.Second) // Default timeout
	})

	session.SetNotifyCallback(func(msg *RPCMessage) {
		s.callback.OnNotify(session, msg)
	})

	// Start session
	if err := session.Start(); err != nil {
		fmt.Printf("Failed to start session: %v\n", err)
		conn.Close()
		s.sessions.Delete(sessionID)
		s.sessionID.Add(-1) // Decrease session count
		return
	}
}

// Broadcast sends a notification to all connected sessions
func (s *RPCServer) Broadcast(msg *RPCMessage) {
	if msg == nil {
		return
	}

	s.sessions.Range(func(key, value interface{}) bool {
		if session, ok := value.(*RPCSession); ok {
			// 忽略发送错误，继续广播给其他会话
			_ = session.Notify(msg)
		}
		return true
	})
}

// GetSessionCount returns the current number of active sessions
func (s *RPCServer) GetSessionCount() int64 {
	return s.sessionID.Load()
}

// GetSession returns a session by its ID
func (s *RPCServer) GetSession(sessionID int64) (*RPCSession, bool) {
	if value, ok := s.sessions.Load(sessionID); ok {
		if session, ok := value.(*RPCSession); ok {
			return session, true
		}
	}
	return nil, false
}

// Issue sends a notification to a specific session
func (s *RPCServer) Issue(session *RPCSession, msg *RPCMessage) error {
	if session == nil {
		return fmt.Errorf("session is nil")
	}
	return session.Notify(msg)
}

// CallMethod performs an RPC call to a specific session
func (s *RPCServer) CallMethod(session *RPCSession, inMsg *RPCMessage, outMsg *RPCMessage, timeout time.Duration) error {
	if session == nil {
		return fmt.Errorf("session is nil")
	}

	return session.CallMethod(inMsg, outMsg, timeout)
}
